/**
 * 
 */
package com.beta.restapi.internal.auth;

import com.alibaba.fastjson.JSON;
import com.beta.em.UserType;
import com.beta.entity.Users;
import com.beta.restapi.internal.Consts;
import com.beta.service.IUsersService;
import com.beta.entity.Couriers;
import com.beta.service.ICouriersService;
import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.security.auth.login.AccountNotFoundException;
import javax.security.auth.login.CredentialException;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author vector
 *
 */
@Service
public class AuthService {

	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private IUsersService userService;
	@Autowired
	private ICouriersService couriersService;
	
	private Long tokenExpireSeconds = Consts.DEFAULT_TOKEN_EXPIRE_SECONDS;
	
	public boolean isAuthenticated(HttpServletRequest request) {
		return Objects.nonNull(getCurrentUser(request));
	}
	
	public boolean isAuthenticated(String token) {
		return Objects.nonNull(getCurrentUser(token));
	}

	public AuthResult auth(String username, String password) throws LoginException {
		Assert.hasText(username);
		Assert.hasText(password);

		if (UserType.isCourierType(username)) {
			return authCourier(username.substring(1), password);
		} else {
			return authUser(username, password);
		}
	}

	private AuthResult authCourier(String username, String password) throws LoginException {
        Couriers courier = couriersService.selectOne(new Couriers().setPhone(username));
        if (Objects.isNull(courier)) {
            throw new AccountNotFoundException("用户不存在");
        }
        verifyPassword(password, courier.getEncryptedPassword());
		String cacheKey = cacheUser(UserType.COURIER.getValue(), courier);
		return new AuthResult(cacheKey, UserType.COURIER, courier);
	}

    private AuthResult authUser(String username, String password) throws LoginException {
        Users user = userService.selectOne(new Users().setPhone(username));
        if (Objects.isNull(user)) {
            throw new AccountNotFoundException("用户不存在");
        }
        verifyPassword(password, user.getEncryptedPassword());
        String cacheKey = cacheUser(user.getUserType(), user);
        return new AuthResult(cacheKey, UserType.getUserType(user.getUserType()), user);
    }

	private String cacheUser(String cacheKeyPrefix, Object userObject) {
		String token = StringUtils.join(cacheKeyPrefix, " ", UUID.randomUUID().toString());
		redisTemplate.opsForValue().set(tokenCacheKey(token), JSON.toJSONString(userObject), tokenExpireSeconds.intValue(), TimeUnit.SECONDS);
        return token;
	}

	<T> T getCurrentUser(HttpServletRequest request) {
		Assert.notNull(request);
		String token = getHeaderToken(request);
		return getCurrentUser(token);
	}

	@SuppressWarnings("unchecked")
	public <T> T getCurrentUser(String token) {
		String userJson = redisTemplate.opsForValue().get(tokenCacheKey(token));
		if (StringUtils.isBlank(userJson)) {
			return null;
		}
		if (isCourierToken(token)) {
            return (T) JSON.parseObject(userJson, Couriers.class);
        } else {
            return (T) JSON.parseObject(userJson, Users.class);
        }
	}

	public void updateCurrentUser(HttpServletRequest request) {
		Object userObj = getCurrentUser(request);
		if (Objects.isNull(userObj)) {
			return ;
		}
		if (userObj instanceof Users) {
			userObj = userService.selectById(((Users) userObj).getId());
		} else if (userObj instanceof Couriers) {
			userObj = couriersService.selectById(((Couriers) userObj).getId());
		}
		redisTemplate.opsForValue().set(tokenCacheKey(getHeaderToken(request)), JSON.toJSONString(userObj), tokenExpireSeconds.intValue(), TimeUnit.SECONDS);
	}

    boolean isCourierRequest(HttpServletRequest request) {
        return StringUtils.startsWith(getHeaderToken(request), UserType.COURIER.getValue());
    }

	private boolean isCourierToken(String token) {
        return StringUtils.startsWith(token, UserType.COURIER.getValue());
    }
	
	public void logout(HttpServletRequest request) {
		logout(getHeaderToken(request));
	}
	
	public void logout(String token) {
		if (StringUtils.isBlank(token)) {
			return ;
		}
		
		redisTemplate.delete(tokenCacheKey(token));
	}
	
	/**
	 * @param request
	 * @return
	 */
	private String getHeaderToken(HttpServletRequest request) {
		return request.getHeader(Consts.HEADER_AUTH_KEY);
	}
	
	private String tokenCacheKey(String token) {
		return StringUtils.join(Consts.AUTH_TOKEN_CACHE_KEY_PREFIX, token);
	}

	private void verifyPassword(String password, String encryptedPassword) throws LoginException {
		if (StringUtils.isBlank(password) || StringUtils.isBlank(encryptedPassword) || !BCrypt.checkpw(password, encryptedPassword)) {
			throw new CredentialException("密码错误");
		}
	}

}
